﻿using System;
using System.Text;
using ComHlp.ContentClasses;
using ComHlp.enums;
using ComHlp.Exceptions;
using NLog;

namespace ComHlp.Classes.Parser.Supplementary
{
    internal class ChartParser
    {
        private readonly Logger _log = LogManager.GetCurrentClassLogger();

        /// <summary> parse received chart data 
        /// f.x.:
        /// 00000025TYP=BS;Y=Soll-Mg        ;0027000002;000000011000000000+;0035000005;000000000500000000+;22.05.970035000007;000000100000000000+;28.05.970035000009;000000005000000000+;05.06.970035000011;000000005000000000+;10.06.970035000013;000000020000000000+;18.06.970035000015;000000100000000000+;27.06.970035000017;000000200000000000+;01.08.970035000019;000000010000000000+;10.10.970035000021;000000010010000000+;06.01.980035000023;000000010000000000+;13.01.980035000025;000000001000000000+;19.01.980035000027;000000010000000000+;23.01.980035000030;000000001000000000+;04.02.980035000032;000000010000000000+;22.04.980035000034;000000025000000000+;23.04.980035000036;000000001000000000+;03.06.980035000038;000000001000000000+;06.05.060035000040;000000040000000000+;19.12.060035000042;000000000000000000+;21.08.080035000044;000000002225000000+;21.10.100035000046;000000001000000000+;15.09.159999
        /// Zeile 1
        /// Typ BS = Balken; T=Torte, Z=Zeitreihe
        /// Y = was wird in Y dargestellt; der Rest bildet X
        /// Folgezeilen
        /// Ident; Wert,Bezeichnung
        /// Ident       wird bei einem Click zurückgegeben
        /// Wert		in 12,6 + Vorzeichen
        /// Bez     Text für den Balken
        /// 
        /// Die Werte sind absteigend zu sortieren und anzuzeigen.
        /// </summary>
        /// <param name="fileContent"></param>
        public ItxChartData ParseChartData(string fileContent)
        {
            ItxChartData chartData = new ItxChartData();
            StringBuilder sbData = new StringBuilder(fileContent);

            // get length of header
            int headerLength = ParseHelper.String2Int(sbData.ToString(0, 8));
            sbData.Remove(0, 8);

            // cut header
            string header = sbData.ToString(0, headerLength);
            sbData.Remove(0, headerLength);

            // analyze header (chart type)
            chartData.Type = ParseType(header);

            // bar & pie have same parsing logic
            if (chartData.Type == ItxChartType.Bar || chartData.Type == ItxChartType.Pie)
            {
                // search for Y= : [TYP=T ;Y=Soll-Mg        ;]
                chartData.SingleYAxisValue = ParseYAxisValue(header);
            }
            else
            {
                /* time is more complicated:
                    [TYP=ZR;MENGE,Menge,Y;REDAT,Datum,ID,;]
                    [TYP=ZR;MENGE,Menge,Y;ART,Artikel,X;REDAT,Datum,ID,;] */
                // split teh spli
                string[] timeHeaderValues = header.Split(';');
                // idx 0 == TYP=ZR

                foreach (var splitValue in timeHeaderValues)
                {
                    // now split again with ,
                    string[] singleHeaderValues = splitValue.Split(',');
                    if (singleHeaderValues.Length < 3)
                        continue;

                    switch (singleHeaderValues[2])
                    {
                        case "Y":
                            chartData.YValueFieldName = singleHeaderValues[0];
                            chartData.YValueFieldDesc = singleHeaderValues[1];
                            break;
                        case "X":
                            chartData.XValueFieldName = singleHeaderValues[0];
                            chartData.XValueFieldDesc = singleHeaderValues[1];
                            break;
                        case "ID":
                            chartData.IdValueFieldName = singleHeaderValues[0];
                            chartData.IdValueFieldDesc = singleHeaderValues[1];
                            break;
                    }
                }
            }

            /* 0027000002;000000011000000000+;
               0035000005;000000000500000000+;22.05.97
               0035000007;000000100000000000+;28.05.97
               0035000009;000000005000000000+;05.06.97
               0035000011;000000005000000000+;10.06.97
               0035000013;000000020000000000+;18.06.97
               0035000015;000000100000000000+;27.06.97 */
            while (sbData.Length > 0)
            {
                int length = ParseHelper.String2Int(sbData.ToString(0, 4));
                sbData.Remove(0, 4);

                // stop?
                if (length == 9999 || length == 0)
                    break;

                // get current entry 
                string currentEntryStr = sbData.ToString(0, length);
                sbData.Remove(0, length);

                // split
                string[] splitEntry = currentEntryStr.Split(';');
                ItxChartDataSingleValue singleValue = new ItxChartDataSingleValue {Ident = splitEntry[0]};
                if (splitEntry.Length > 2)
                    singleValue.Description = splitEntry[2];

                // inside the documentation 12,6 are stated - but this does not match with current clients display
                long preComma = ParseHelper.String2Long(splitEntry[1].Substring(0, 11));
                long postComma = ParseHelper.String2Long(splitEntry[1].Substring(11, 7));
                float value;
                if (!float.TryParse($"{preComma},{postComma}", out value))
                {
#if DEBUG
                    _log.Debug($"Unable to convert to float: {splitEntry[1]}");
#endif
                    value = 0;
                }

                // check for sign?"000000000799400000+"
                if (splitEntry[1].Length >= 19 && splitEntry[1][18] == '-')
                    value = value*-1;

                // parse value (6 pre, 4 post, one sign)
                singleValue.Value = value;
                chartData.Values.Add(singleValue);
            }

            return chartData;
        }

        /// <summary> parse chart type 
        /// 00000025TYP=BS;Y=Soll-Mg        ;
        /// Zeile 1: Typ BS = Balken; T=Torte, Z=Zeitreihe </summary>
        /// <param name="chartHeader"></param>
        /// <returns></returns>
        private ItxChartType ParseType(string chartHeader)
        {
            // search for TYP=
            int typePos = chartHeader.IndexOf("TYP=", StringComparison.Ordinal);
            if (typePos == -1)
                throw new CommunicationException("Chart data does not contain any chart type");

            // search for first ;
            int semicolonPos = chartHeader.IndexOf(";", StringComparison.Ordinal);
            if (semicolonPos == -1)
                semicolonPos = chartHeader.Length;

            // cut type
            string typeStr = chartHeader.Substring(typePos + 4, semicolonPos - typePos - 4);
            switch (typeStr)
            {
                case "BS":
                    return ItxChartType.Bar;
                case "T ":
                    return ItxChartType.Pie;
                case "ZR":
                    return ItxChartType.Time;
                default:
#if DEBUG
                    _log.Warn($"Invalid chart type: {typeStr}");
#endif
                    return ItxChartType.Bar;
            }
        }

        /// <summary> parse single y-axis value
        /// 00000025TYP=BS;Y=Soll-Mg        ;
        /// Zeile 1: Typ BS = Balken; T=Torte, Z=Zeitreihe </summary>
        /// <param name="chartHeader"></param>
        /// <returns></returns>
        private string ParseYAxisValue(string chartHeader)
        {
            // search for Y=
            int yPos = chartHeader.IndexOf("Y=", StringComparison.Ordinal);
            if (yPos == -1)
                throw new CommunicationException("Chart data does not contain y axis value");

            // search for ; after ypos
            int semicolonPos = chartHeader.IndexOf(";", yPos, StringComparison.Ordinal);
            if (semicolonPos == -1)
                semicolonPos = chartHeader.Length;

            // cut type
            string yAxisValue = chartHeader.Substring(yPos + 2, semicolonPos - yPos - 2).Trim();
            return yAxisValue;
        }
    }
}